## /usr/bin/env python    
# -*- coding: utf-8 -*-
# vi: ts=4 sw=4

#-------------------------------------------------------------------------------
# Program: PuttyKnife VPN 
# Module:  Main
# Desc:    PuttyKnife VPN is a PuTTY/ssh-based virtual private networking
#          client -- specifically, it is a GTK2 and Tk wrapper around plink
# Author:  Rob A. Shinn
#
# Legal:    Copyright 2008 Rob A. Shinn. All rights reserved.
#           This program is free software; you can redistribute it and/or modify
#           it under the terms of the GNU General Public License as published by
#           the Free Software Foundation; either version 3 of the License, or
#           (at your option) any later version.
#
#           This program is distributed in the hope that it will be useful,
#           but WITHOUT ANY WARRANTY; without even the implied warranty of
#           MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#           GNU General Public License for more details.
#-------------------------------------------------------------------------------

import sys
import re
import gtk
import gobject
import datetime
import time
from GladeWindow import *
from configuration import config
from service_def import services
from connect_tcl_wrap import *
from logger import *
from dialogs import *
import gtkexcepthook

sys.excepthook = gtkexcepthook.info
class GUI_Log(Log):
    def __init__(self, TextBuffer, TextView, *args):
        self.TextBuffer = TextBuffer
        self.TextView = TextView
        Log.__init__(self, *args)
    def write(self, text, level=3):
        datestamp = datetime.datetime.today().strftime(self.date_format)
        caller = self.caller()
        msg = '[%s][%s] %s: %s\n' % (datestamp, levels[level], caller, text)
        the_end=self.TextBuffer.get_end_iter()
        self.TextBuffer.place_cursor(the_end)
        self.TextBuffer.insert_at_cursor(msg)
        the_end=self.TextBuffer.get_end_iter()
        mark=self.TextBuffer.create_mark('the end',the_end,True)
        self.TextView.scroll_to_mark(mark,0.0)
        Log.write(self, text, level, caller)

class MainWindow(GladeWindow):
    def __init__(self):

        ''' '''
        
        self.init()

    def init(self):

        filename = os.environ['PUTTYKNIFE_HOME']+r'/puttyknife.glade'

        widget_list = [
            'MainWindow',
            'profile_entry',
            'profile_add_button',
            'profile_remove_button',
            'profile_edit_button',
            'profile_comboxentry',
            'service_combobox',
            'server_entry',
            'port_spinbutton',
            'delay_spinbutton',
            'compression_checkbox',
            'x11_forward_checkbox',
            'verbose_checkbox',
            'agent_forward_checkbox',
            'pty_allocate_checkbox',
            'username_entry',
            'password_entry',
            'use_key_checkbutton',
            'keyfile_entry',
            'keyfile_browse_button',
            'tunnel_add_button',
            'tunnel_remove_button',
            'tunnel_edit_button',
            'tunnel_clear_button',
            'tunnels_treeview',
            'listen_ip_entry',
            'listen_port_spinbox',
            'host_entry',
            'host_port_spinbox',
            'socks_port_checkbutton',
            'socks_port_spinbutton',
            'messages_textview',
            'clear_log_button',
            'log_to_file_checkbox',
            'log_file_entry',
            'log_file_browse_button',
            'connect_button',
            'save_button',
            'close_button',
            'cancel_button',
            'open_button',
            'cancel_button',
            'save_as_button',
            'protocol_combobox',
            'auto_connect_checkbox',
            'local_radiobutton',
            'remote_radiobutton',
            'FileOpenDialog',
            'FileSaveAsDialog',
            'proxy_type_combobox',
            'proxy_host_entry',
            'proxy_port_spinbutton',
            'proxy_username_entry',
            'proxy_password_entry',
            'proxy_command_entry',
            'proxy_dns_checkbutton',
            'statusbar1',
            'auto_reconnect_checkbutton',
            'disable_shell_checkbutton',
            'use_agent_checkbutton',
            ]

        handlers = [
            'on_profile_comboxentry_changed',
            'on_profile_entry_changed',
            'on_profile_add_button_clicked',
            'on_profile_remove_button_clicked',
            'on_profile_edit_button_clicked',
            'on_service_combobox_changed',
            'on_protocol_combobox_changed',
            'on_tunnels_treeview_select_cursor_row',
            'on_tunnels_treeview_cursor_changed',
            'on_keyfile_browse_button_clicked',
            'on_tunnel_add_button_clicked',
            'on_tunnel_remove_button_clicked',
            'on_tunnel_edit_button_clicked',
            'on_tunnel_clear_button_clicked',
            'on_socks_port_checkbutton_toggled',
            'on_clear_log_button_clicked',
            'on_log_to_file_checkbox_toggled',
            'on_use_key_checkbutton_toggled',
            'on_log_file_browse_button_clicked',
            'on_connect_button_clicked',
            'on_save_button_clicked',
            'on_close_button_clicked',
            'on_cancel_button_clicked',
            'on_open_button_clicked',
            'on_save_as_button_clicked',
            'on_MainWindow_delete_event',
            ]
        self.ssh_combobox_populated = False
        self.profile_combobox_populated = False
        self.connected = False
        top_window = 'MainWindow'
        GladeWindow.__init__(self, filename, top_window, widget_list, handlers)
 
    def __get_setting(self, setting):
        return config.setting['%s.%s' % (self.current_profile, 
                                         setting)]
    def __get_tunnel(self, tunnel, setting):
        return config.setting['%s.%s' % (tunnel, 
                                         setting)]

    def on_MainWindow_delete_event(self, *args):
        ''' avoids destruction of main window '''
        self.on_close_button_clicked()
        
    def on_profile_comboxentry_changed(self, *args):
        ''' change the current profile, if appropriate '''
        iter = self.widgets['profile_comboxentry'].get_active_iter()
        if iter:
            profile = self.profile_list.get_value(iter, 0)
            self.widgets['profile_entry'].set_text(profile)
            self.set_default_profile(profile)
            
    def on_service_combobox_changed(self, *args):
        iter = self.widgets['service_combobox'].get_active_iter()
        if iter:
            service = self.service_list.get_value(iter, 0)
            port = services[service]
            self.widgets['listen_port_spinbox'].set_value(port)
            self.widgets['host_port_spinbox'].set_value(port)
            self.widgets['listen_ip_entry'].set_text('127.0.0.1')
            host = self.widgets['server_entry'].get_text()
            self.widgets['host_entry'].set_text(host)         
        
    def on_tunnel_add_button_clicked(self, *args):
        iter = self.widgets['service_combobox'].get_active_iter()
        if iter:
            service_name=self.service_list.get_value(iter, 0)
        else:
            service_name="Other"
        listen_ip=self.widgets['listen_ip_entry'].get_text()
        listen_port=self.widgets['listen_port_spinbox'].get_value()
        host=self.widgets['host_entry'].get_text()
        host_port=self.widgets['host_port_spinbox'].get_value()
        if self.widgets['local_radiobutton'].get_active():
            forward='local'
        else:
            forward='remote'
        self.tunnel_list.append([service_name,
                                 listen_ip,
                                 listen_port,
                                 host,
                                 host_port,
                                 forward])
    
    def on_tunnel_remove_button_clicked(self, *args):
        selection=self.widgets['tunnels_treeview'].get_selection()
        model, iter = selection.get_selected()
        model.remove(iter)

    def on_tunnel_edit_button_clicked(self, *args):
        selection=self.widgets['tunnels_treeview'].get_selection()
        model, iter = selection.get_selected()
        iter2 = self.widgets['service_combobox'].get_active_iter()
        if iter2:
            service_name=self.service_list.get_value(iter2, 0)
        else:
            service_name="Other"
        listen_ip=self.widgets['listen_ip_entry'].get_text()
        listen_port=self.widgets['listen_port_spinbox'].get_value()
        host=self.widgets['host_entry'].get_text()
        host_port=self.widgets['host_port_spinbox'].get_value()
        if self.widgets['local_radiobutton'].get_active():
            forward='local'
        else:
            forward='remote'
        model.set(iter, 
                   0, service_name,
                   1, listen_ip,
                   2, listen_port,
                   3, host,
                   4, host_port,
                   5, forward)
                                                         
    def on_tunnel_clear_button_clicked(self, *args):
        self.tunnel_list.clear()
        
    def __get_service_combobox_iter(self, combobox, model, service):
        list_model = model
        i = list_model.get_iter_root()
        iter = None
        while i:
            if list_model.get_value(i, 0) == service:
                iter = i
            #logfile.write("%s %s" % (i, service))
            i = list_model.iter_next(i)
        if iter:
            return iter
        else:
            logfile.write('Could not determine a value for %s' % combobox,
                          level.ERROR)
            
    def on_tunnels_treeview_select_cursor_row(self, *args):
        pass
    
    def on_tunnels_treeview_cursor_changed(self, *args):
        selection = self.widgets['tunnels_treeview'].get_selection()
        model, iter = selection.get_selected()
        service = model.get_value(iter, 0)
        listen_ip = model.get_value(iter, 1)
        listen_port = model.get_value(iter, 2)
        host = model.get_value(iter, 3)
        host_port = model.get_value(iter, 4)
        forward_type = model.get_value(iter, 5)
        combobox = self.widgets['service_combobox']
        iter = self.__get_service_combobox_iter(combobox, self.service_list, service)
        self.widgets['service_combobox'].set_active_iter(iter)
        self.widgets['listen_ip_entry'].set_text(listen_ip)
        self.widgets['listen_port_spinbox'].set_value(listen_port)
        self.widgets['host_entry'].set_text(host)
        self.widgets['host_port_spinbox'].set_value(host_port)
    
    def on_socks_port_checkbutton_toggled(self, *args):
        pass
    
    def on_clear_log_button_clicked(self, *args):
        self.messages.delete(self.messages.get_start_iter(),
                             self.messages.get_end_iter())
    
    def on_log_file_browse_button_clicked(self, *args):
        if sys.platform == 'win32':
            homedir=os.environ['HOMEDRIVE']+os.environ['HOMEPATH']
        else: 
            # XXX assume POSIX?! XXX
            homedir=os.environ['HOME']
        self.widgets['FileSaveAsDialog'].set_property("title","Save log file as")
        self.widgets['FileSaveAsDialog'].set_current_folder(homedir)
        self.widgets['FileSaveAsDialog'].show()
    
    def on_connect_button_clicked(self, *args):
        if not self.connected:
            self.widgets['connect_button'].set_label('gtk-disconnect')
            self.widgets['connect_button'].set_use_stock(True)
            self.connect()
        else:
            self.disconnect()
            self.widgets['connect_button'].set_label('gtk-connect')
            self.widgets['connect_button'].set_use_stock(True)
        
    def on_profile_entry_changed(self, *args):
        pass
    
    def on_profile_add_button_clicked(self, *args):
        ''' add a new profile and set the current profile to it '''
        profile = self.widgets['profile_entry'].get_text()
        if not config.has_section(profile):
            config.new_profile(profile)
            self.set_default_profile(profile)
        else:
            dialog = ErrorDialog(title='Putty Knife VPN - Error',
                               parent=self.widgets['MainWindow'],
                               message='Profile "%s" already exists' % profile)
            dialog.run()
            dialog.destroy()
            
    def on_profile_remove_button_clicked(self, *args):
        ''' remove a profile by removing its section from the config.  also
            removes the profile from the config.profiles list and the profile
            comboboxentry.  The config.setting entries remain in memory for
            future undo functionality '''
        dialog = QueryDialog(
             title='Putty Knife VPN - Confirm',
             parent=self.widgets['MainWindow'],
             message='Profile "%s" will be permanently deleted!' % self.current_profile)
        response = dialog.run()
        dialog.destroy()
        if response: 
           profile = config.profiles[0]
           if profile == self.current_profile:
               profile = config.profiles[1]
           config.remove_section(self.current_profile)
           config.profiles.remove(self.current_profile)
           self.set_default_profile(profile)       
            
    def on_profile_edit_button_clicked(self, *args):
        oldprofile = self.current_profile
        profile = self.widgets['profile_entry'].get_text()
        if not config.has_section(profile):
            config.new_profile(profile)
            self.save_profile(profile)
            config.remove_section(oldprofile)
            config.profiles.remove(oldprofile)
            self.set_default_profile(profile)
        else:
            dialog=ErrorDialog(title='Putty Knife - Error',
                               message='Profile "%s" already exists.' % profile,
                               parent=self.widgets['MainWindow'])
            dialog.run()
            dialog.destroy()
    
    def on_protocol_combobox_changed(self, *args):
        pass
    
    def on_keyfile_browse_button_clicked(self, *args):
        if sys.platform == 'win32':
            homedir=os.environ['HOMEDRIVE']+os.environ['HOMEPATH']
        else: 
            # XXX assume POSIX?! XXX
            homedir=os.environ['HOME']
        self.widgets['FileOpenDialog'].set_property("title","Select private key")
        self.widgets['FileOpenDialog'].set_current_folder(homedir)
        self.widgets['FileOpenDialog'].show()
        
    def on_use_key_checkbutton_toggled(self, checkbox, *args):
        if not self.widgets['keyfile_entry'].get_text():
            checkbox.set_active(False)
         
    def on_log_to_file_checkbox_toggled(self, checkbox, *args):
        if not self.widgets['log_file_entry'].get_text():
            checkbox.set_active(False)
        else:
            if checkbox.get_active():
                logfilename=self.widgets['log_file_entry'].get_text()
                if logfilename:
                    logfile.open(logfilename)
            else:
                if logfile.filename:
                    logfile.close()                      
    
    def __save_entry(self, entry, setting):
        ''' save an entry into the configuration model '''
        config.setting[setting] = self.widgets[entry].get_text()
        
    def __save_checkbutton(self, checkbutton, setting):
        ''' save a checkbox state (True/False into the configuration model '''
        config.setting[setting] = self.widgets[checkbutton].get_active()
        
    def __save_spinbutton(self, spinbutton, setting):
        ''' save the value of a spinbox into the config model '''
        config.setting[setting] = self.widgets[spinbutton].get_value_as_int()
        
    def __save_static_combobox(self, combobox, model, setting, key=1):
        ''' save the current selected item in a static combobox. '''
        i = self.widgets[combobox].get_active_iter()
        # XXX there should be a better way to do this XXX
        if key == 1:
            config.setting[setting] = eval(model.get_value(i, key))
        else:
            config.setting[setting] = model.get_value(i, key)
        
    def __save_tunnels(self, profile):
        config.clear_tunnels(profile)
        i = self.tunnel_list.get_iter_root()
        iter = None
        index=0
        while i:
            index += 1
            tunnel=config.new_tunnel(profile, index)
            config.setting['%s.service desc' % tunnel] = \
                self.tunnel_list.get_value(i, 0)
            config.setting['%s.source address' % tunnel] = \
                self.tunnel_list.get_value(i, 1)
            config.setting['%s.source port' % tunnel] = \
                self.tunnel_list.get_value(i, 2)
            config.setting['%s.destination address' % tunnel] = \
                self.tunnel_list.get_value(i, 3)
            config.setting['%s.destination port' % tunnel] = \
                self.tunnel_list.get_value(i, 4)
            config.setting['%s.forward type' % tunnel] = \
                self.tunnel_list.get_value(i, 5)
            i = self.tunnel_list.iter_next(i)
    
    def connect(self):
        global b, logfile
        connection = TCL_Wrap_Connection()
        #connection.host=self.__get_setting('server')
        connection.host=self.widgets['server_entry'].get_text()
        connection.port=self.widgets['port_spinbutton'].get_value_as_int()
        if self.widgets['use_key_checkbutton'].get_active():
            connection.keyfile=self.widgets['keyfile_entry'].get_text()
        else:
            connection.password=self.widgets['password_entry'].get_text()
        connection.compression=self.widgets['compression_checkbox'].get_active()
        connection.x11=self.widgets['x11_forward_checkbox'].get_active()
        if self.widgets['socks_port_checkbutton'].get_active():
            connection.socks_port=self.widgets['socks_port_spinbutton'].get_value_as_int()
        connection.agentfwd=self.widgets['agent_forward_checkbox'].get_active()
        connection.agent=self.widgets['use_agent_checkbutton'].get_active()
        connection.shell=not self.widgets['disable_shell_checkbutton'].get_active()
        connection.pty=self.widgets['pty_allocate_checkbox'].get_active()
        connection.logfile=logfile
        connection.username=self.widgets['username_entry'].get_text()
        connection.proxy_host=self.widgets['proxy_host_entry'].get_text()
        connection.proxy_host=self.widgets['proxy_username_entry'].get_text()
        connection.proxy_password=self.widgets['proxy_password_entry'].get_text()
        connection.proxy_port=self.widgets['proxy_port_spinbutton'].get_value_as_int()
        connection.proxy_command=self.widgets['proxy_command_entry'].get_text()
        connection.create_putty_session('PuttyKnife_VPN_Session')
        tunnels=Tunnels()
        for tunnel in config.get_tunnels(self.current_profile):
            new_tunnel=Tunnel(listen_addr=self.__get_tunnel(tunnel, 
                                                            'source address'), 
                              listen_port=self.__get_tunnel(tunnel,
                                                            'source port'),
                              host_addr=self.__get_tunnel(tunnel, 
                                                          'destination address'),
                              host_port=self.__get_tunnel(tunnel, 
                                                          'destination port'),
                              remote=self.__get_tunnel(tunnel, 'forward type') == 'remote')
            tunnels.add_tunnel(new_tunnel)
        connection.tunnels=tunnels
        self.connected=True
        self.widgets['statusbar1'].push(self.sb_context,'Connecting')
        self.remote_connection=False
        while b and self.connected:
            self.connection=connection
            self.connection.open()
            msg=''
            ch=''
            returncode=None
            #global b, logfile
            logfile.write('\n')
            while (not returncode) and (b) and (self.connected):
                try:
                    ch = async_subprocess.recv_some(self.connection.child, 
                                                    t=0.01, tr=5)
                except Exception, errmsg:
                    self.connection.child.communicate()
                    rc=self.connection.child.returncode
                    logfile.write('Remote host terminated connection, rc=%s' % rc)
                    logfile.write(errmsg,level.ERROR)
                    if not self.widgets['auto_reconnect_checkbutton'].get_active():
                        self.widgets['connect_button'].set_label('gtk-connect')
                        self.widgets['connect_button'].set_use_stock(True)
                        self.connected=False
                        self.disconnect(why='Remote host terminated connection. rc=%s' % rc)
                    else:
                        self.widgets['connect_button'].set_sensitive(False)
                        self.widgets['statusbar1'].pop(self.sb_context)
                        self.widgets['statusbar1'].push(self.sb_context,'Waiting for re-connect')
                        i=0
                        secs=self.widgets['delay_spinbutton'].get_value_as_int()
                        while i <= self.widgets['delay_spinbutton'].get_value_as_int():
                            self.widgets['statusbar1'].push(self.sb_context, 
                                'Waiting for re-connect: %s seconds' %  (secs-i))
                            gtk.main_iteration_do(False)
                            time.sleep(1)
                            self.widgets['statusbar1'].pop(self.sb_context)
                            i += 1
                        self.widgets['statusbar1'].pop(self.sb_context)
                        if self.remote_connection:
                            self.widgets['statusbar1'].pop(self.sb_context)
                            self.remote_connection=False
                        self.widgets['connect_button'].set_sensitive(True)
                q=re.compile('^.*Connection is stable.$')
                for c in ch:
                    if c=='\n': 
                        if q.match(msg):
                            self.widgets['statusbar1'].push(self.sb_context,'Connected')
                            self.remote_connection=True
                        logfile.write(msg)
                        msg=''
                    else:    
                        msg=msg+c
                if not returncode:
                    returncode=self.connection.child.returncode
                gtk.main_iteration_do(False)  
        
    def disconnect(self, why="Terminated connection at user request."):
        if self.connected:
            self.connection.child.kill()
            logfile.write(why,level.INFO)
            self.widgets['connect_button'].set_label('gtk-connect')
            self.widgets['connect_button'].set_use_stock(True)
        self.connected=False
        self.widgets['statusbar1'].pop(self.sb_context)
        if self.remote_connection:
            self.widgets['statusbar1'].pop(self.sb_context)
            self.remote_connection=False
        
        
    def save_profile(self, profile):
        ''' saves the current profile into the configuration model '''
        self.__save_entry('server_entry', '%s.server' % profile)
        self.__save_entry('username_entry', '%s.username' % profile)
        self.__save_entry('password_entry', '%s.password' % profile)
        self.__save_entry('keyfile_entry', '%s.keyfile' % profile)
        self.__save_entry('log_file_entry', '%s.logfile' % profile)
        self.__save_entry('proxy_host_entry', 
                          '%s.proxy host' % profile)
        self.__save_spinbutton('proxy_port_spinbutton', 
                          '%s.proxy port' % profile)
        self.__save_entry('proxy_username_entry',
                          '%s.proxy username' % profile)
        self.__save_entry('proxy_password_entry',
                          '%s.proxy password' % profile)        
        self.__save_entry('proxy_command_entry',
                          '%s.proxy command' % profile)        
        self.__save_spinbutton('port_spinbutton', '%s.port' % profile)
        self.__save_checkbutton('log_to_file_checkbox', '%s.log to file' % profile)
        self.__save_static_combobox('protocol_combobox', self.protocol_list,
                                    '%s.ssh version' % profile)
        model=self.widgets['proxy_type_combobox'].get_model()
        self.__save_static_combobox('proxy_type_combobox', model,
                                    '%s.proxy method' % profile,key=0)
        self.__save_checkbutton('compression_checkbox',
                               '%s.compression' % profile)
        self.__save_checkbutton('x11_forward_checkbox',
                                '%s.forward x11' % profile)
        self.__save_checkbutton('agent_forward_checkbox', 
                              '%s.forward agent' % profile)
        self.__save_checkbutton('pty_allocate_checkbox',
                                '%s.disable pty' % profile)
        self.__save_checkbutton('use_key_checkbutton',
                               '%s.private key' % profile)
        self.__save_checkbutton('socks_port_checkbutton', 
                                '%s.use socks port' % profile)
        self.__save_checkbutton('log_to_file_checkbox', 
                                '%s.log to file' % profile)
        self.__save_spinbutton('socks_port_spinbutton', 
                               '%s.socks port' % profile)
        self.__save_checkbutton('verbose_checkbox', '%s.verbose' % profile)
        self.__save_checkbutton('auto_reconnect_checkbutton', 
                                '%s.auto reconnect' % profile)
        self.__save_checkbutton('auto_connect_checkbox', "global.auto connect")
        self.__save_spinbutton('delay_spinbutton', '%s.retry delay' % profile)
        self.__save_tunnels(profile)
                 
         
    def on_save_button_clicked(self, *args):
        ''' save the current profile and write out the .ini file '''
        self.save_profile(self.current_profile)
        config.write()
    
    def on_close_button_clicked(self, *args):
        ''' go bye-bye '''
        if self.connected:
            self.connection.child.kill()
        global b
        b=False
    
    def on_cancel_button_clicked(self, button, *args):
        parent=button.get_parent()
        parent=parent.get_parent()
        parent=parent.get_parent()
        print self.widgets['FileSaveAsDialog']
        parent.hide()
    
    def on_open_button_clicked(self, button, *args):
        parent=button.get_parent()
        parent=parent.get_parent()
        parent=parent.get_parent()
        keyfile=parent.get_filename()
        self.widgets['keyfile_entry'].set_text(keyfile)
        parent.hide()
    
    def on_save_as_button_clicked(self, button, *args):
        parent=button.get_parent()
        parent=parent.get_parent()
        parent=parent.get_parent()
        logfilename=parent.get_filename()
        self.widgets['log_file_entry'].set_text(logfilename)
        parent.hide()
    
    def __populate_ssh_combobox(self):
        if not self.ssh_combobox_populated:
            protocols = ['SSH v1', 'SSH v2']
            self.protocol_list = gtk.ListStore(gobject.TYPE_STRING,
                                             gobject.TYPE_STRING)
            i = 0
            for protocol in protocols:
                i += 1
                self.protocol_list.append([protocol, "%s" % i])
            self.widgets['protocol_combobox'].set_model(self.protocol_list)
            cell = gtk.CellRendererText()
            self.widgets['protocol_combobox'].pack_start(cell, True)
            self.widgets['protocol_combobox'].add_attribute(cell, 'text', 0)
            self.ssh_combobox_populated = True
            
    def __populate_service_combobox(self):
        self.service_list = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_INT)
        servicekeys=services.keys()
        servicekeys.sort()
        for service in servicekeys:
            self.service_list.append([service,services[service]]) 
        self.widgets['service_combobox'].set_model(self.service_list)
        cell = gtk.CellRendererText()
        self.widgets['service_combobox'].clear()
        self.widgets['service_combobox'].pack_start(cell, True)
        self.widgets['service_combobox'].add_attribute(cell, 'text', 0)
        
        
    def __populate_profile_combobox(self):
        self.profile_list = gtk.ListStore(gobject.TYPE_STRING)
        for profile in config.profiles:
            self.profile_list.append([profile])
        self.widgets['profile_comboxentry'].set_model(self.profile_list)
        cell = gtk.CellRendererText()
        self.widgets['profile_comboxentry'].clear()
        self.widgets['profile_comboxentry'].pack_start(cell, True)
        self.widgets['profile_comboxentry'].add_attribute(cell, 'text', 0)
         
    def __populate_static_combobox(self, combobox, model, setting, key=1):
        if config.setting.has_key(setting):
            list_model = model
            i = list_model.get_iter_root()
            iter = None
            while i:
                if list_model.get_value(i, key) == "%s" % config.setting[setting]:
                    iter = i
                logfile.write("%s %s" % (i, setting))
                i = list_model.iter_next(i)
            if iter:
                self.widgets[combobox].set_active_iter(iter)
            else:
                logfile.write('Could not determine a value for %s' % combobox,
                              level.ERROR)
    
    def __populate_entry(self, entry, setting):
        if not config.setting.has_key(setting):
            self.widgets[entry].set_text('')
        else:
            if config.setting[setting]:
                self.widgets[entry].set_text('%s' % config.setting[setting])
            else:
                self.widgets[entry].set_text('')
            
    def __populate_checkbox(self, checkbox, setting):  
        self.widgets[checkbox].set_active(config.setting[setting])
        
    def __populate_spinbox(self, spinbox, setting):
        if config.setting.has_key(setting):
            self.widgets[spinbox].set_value(config.setting[setting])
        
    def __populate_tunnels(self, profile):
        ''' populates the tunnels TreeView from the profile "profile" '''
        self.tunnel_list=gtk.ListStore(gobject.TYPE_STRING, # service desc
                                       gobject.TYPE_STRING, # listen addr
                                       gobject.TYPE_INT,    # listen port
                                       gobject.TYPE_STRING, # host addr
                                       gobject.TYPE_INT,
                                       gobject.TYPE_STRING)    # host port
        self.widgets['tunnels_treeview'].set_model(self.tunnel_list)
        svd_cell=gtk.CellRendererText()
        tv=self.widgets['tunnels_treeview']
        for column in tv.get_columns():
            tv.remove_column(column)
        tv.insert_column_with_attributes(-1, "Service", svd_cell, text=0, width_chars=40)
        tv.insert_column_with_attributes(-1, "Listen IP", svd_cell, text=1, width_chars=40)
        tv.insert_column_with_attributes(-1, "Port", svd_cell, text=2, width_chars=6)
        tv.insert_column_with_attributes(-1, "Hostname/IP", svd_cell, text=3, width_chars=40)
        tv.insert_column_with_attributes(-1, "Port", svd_cell, text=4, width_chars=6)
        tv.insert_column_with_attributes(-1, "Forward", svd_cell, text=5)
        tunnels=config.get_tunnels(profile)
        for tunnel in tunnels:
            service_desc=config.setting['%s.service desc' % tunnel]
            listen_ip=config.setting['%s.source address' % tunnel]
            listen_port=config.setting['%s.source port' % tunnel]
            host=config.setting['%s.destination address' % tunnel]
            host_port=config.setting['%s.destination port' % tunnel]
            forward_type=config.setting['%s.forward type' % tunnel]
            self.tunnel_list.append([service_desc, 
                                     listen_ip,
                                     listen_port,
                                     host,
                                     host_port,
                                     forward_type])
        
    def populate_controls(self, profile):                                                                                                     
        self.__populate_profile_combobox()
        self.__populate_ssh_combobox()
        self.__populate_service_combobox()
        self.__populate_tunnels(profile)
        self.widgets['profile_entry'].set_text(profile)
        self.__populate_entry('server_entry', '%s.server' % profile)
        self.__populate_entry('username_entry', '%s.username' % profile)
        self.__populate_entry('password_entry', '%s.password' % profile)
        self.__populate_entry('keyfile_entry', '%s.keyfile' % profile)
        self.__populate_entry('log_file_entry', '%s.logfile' % profile)
        self.__populate_spinbox('port_spinbutton', '%s.port' % profile)
        self.__populate_entry('proxy_host_entry', '%s.proxy host' % profile)
        self.__populate_entry('proxy_username_entry', 
                              '%s.proxy username' % profile)
        self.__populate_entry('proxy_password_entry', 
                              '%s.proxy password' % profile)
        self.__populate_spinbox('proxy_port_spinbutton', 
                                '%s.proxy port' % profile)
        self.__populate_entry('proxy_command_entry', 
                              '%s.proxy command' % profile)
        model=self.widgets['proxy_type_combobox'].get_model()
        self.__populate_static_combobox('proxy_type_combobox',
                                        model, 
                                        '%s.proxy method' % profile, key=0)
        self.__populate_static_combobox('protocol_combobox',
                                        self.protocol_list, 
                                        '%s.ssh version' % profile)
        self.__populate_checkbox('compression_checkbox',
                                 "%s.compression" % profile)
        self.__populate_checkbox('x11_forward_checkbox',
                                 '%s.forward x11' % profile)
        self.__populate_checkbox('pty_allocate_checkbox',
                                 '%s.disable pty' % profile)
        self.__populate_checkbox('agent_forward_checkbox',
                                 '%s.forward agent' % profile)
        self.__populate_checkbox('auto_connect_checkbox', 'global.auto connect')
        self.__populate_checkbox('use_key_checkbutton', 
                                 '%s.private key' % profile)
        self.__populate_checkbox('log_to_file_checkbox', 
                                 '%s.log to file' % profile)
        self.__populate_checkbox('auto_reconnect_checkbutton', 
                                 '%s.auto reconnect' % profile)
        self.__populate_spinbox('delay_spinbutton', '%s.retry delay' % profile)
          
    def set_default_profile(self, profile=None):
        logfile.write("%s" % config.profiles, level.DEBUG)
        msg = "configuration settings:\n"
        for key in config.setting:
            section, setting = key.split('.')
            if config.has_section(section):
                msg = "%s%s: %s\n" % (msg, key, config.setting[key])
            #else:
                #it's been deleted, so don't show it.
        logfile.write(msg)
        if not profile:
            if config.setting.has_key('global.default profile') and \
                config.setting['global.default profile']:
                self.current_profile = config.setting['global.default profile']
                self.sb_context=\
                    self.widgets['statusbar1'].get_context_id('mainwindow')
                self.widgets['statusbar1'].push(self.sb_context,'Disconnected.')
            else:
                self.current_profile = config.profiles[0]
        else:
            self.current_profile = profile
        config.setting['global.default profile'] = self.current_profile
        self.populate_controls(self.current_profile)
    

        
def main(argv):
    if not os.environ.has_key('PUTTYKNIFE_HOME'):
            os.environ['PUTTYKNIFE_HOME']=os.path.dirname(sys.argv[0])
    w = MainWindow()
    w.messages = gtk.TextBuffer()
    w.widgets['messages_textview'].set_buffer(w.messages)
    global logfile, b
    logfile = GUI_Log(w.messages,w.widgets['messages_textview'])
    logfile.stderr=False
    logfile.stdout=False
    config.logfile=logfile
    w.set_default_profile()
    w.show()
    b=True
    if w.widgets['auto_connect_checkbox'].get_active():
        w.on_connect_button_clicked()
    while b:
        gtk.main_iteration_do()
    
if __name__ == '__main__':
    main(sys.argv)
